package net.xxcxcxcx.xc_enhancedmob.mixin;

import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.damage.EntityDamageSource;
import net.minecraft.entity.damage.ProjectileDamageSource;
import net.minecraft.util.ActionResult;
import net.minecraft.world.World;
import net.xxcxcxcx.xc_enhancedmob.event.*;
import net.xxcxcxcx.xc_enhancedmob.render.damage_num.DamageNumParticle;
import net.xxcxcxcx.xc_enhancedmob.render.damage_num.DamageNumRenderer;
import net.xxcxcxcx.xc_enhancedmob.skills.Ref;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.*;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

@Mixin(LivingEntity.class)
public abstract class LivingEntityMixin extends Entity {
    public boolean candamage, cantreat = false;
    DamageSource source = null;

    public LivingEntityMixin(EntityType<?> type, World world) {
        super(type, world);
    }

    @Shadow
    public abstract @Nullable LivingEntity getPrimeAdversary();

    @Shadow
    protected abstract void initDataTracker();

    @Shadow
    protected abstract float applyArmorToDamage(DamageSource source, float amount);

    @Shadow
    public abstract void setHealth(float health);

    @Shadow
    public abstract boolean isDead();

    //移除无敌帧
    @ModifyConstant(method = "damage", constant = @Constant(intValue = 20, ordinal = 0))
    public int ChangeInvincibleFrame(int constant) {
        return 0;
    }

    //开始攻击技能触发
    @Inject(method = "onAttacking", at = @At(value = "FIELD", target = "net/minecraft/entity/LivingEntity.attacking : Lnet/minecraft/entity/LivingEntity;", ordinal = 0), cancellable = true)
    private void applyOnAttackAbilities(Entity target, CallbackInfo ci) {
        ActionResult result = onStartAttackCallback.EVENT.invoker().onStartAttack((LivingEntity) (Object) this, (LivingEntity) target);
        if (result == ActionResult.FAIL) {
            ci.cancel();
        }
    }

    //提前获取source信息用来进行防御和攻击验证
    @Inject(method = "damage", at = @At(value = "HEAD"))
    private void applyaddsource(DamageSource damageSource, float amount, CallbackInfoReturnable<Boolean> cir) {
        source = damageSource;
        candamage = true;
    }

    //防御技能触发
    @ModifyVariable(method = "damage", at = @At(value = "FIELD", target = "Lnet/minecraft/entity/LivingEntity;despawnCounter:I"), ordinal = 0, argsOnly = true)
    private float applyOnDamageAbilities(float amount) {
        if (source == null) return amount;
        Ref<Float> floatRef = new Ref<>(amount);
        ActionResult result = onDamagedCallback.EVENT.invoker().onDamaged(
                (source.getAttacker() instanceof LivingEntity livingEntity) ? livingEntity : null,
                (LivingEntity) (Object) (this),
                source,
                floatRef,
                source instanceof EntityDamageSource,
                source instanceof ProjectileDamageSource);
        amount = floatRef.getValue();
        if (result == ActionResult.FAIL) {
            candamage = false;
            return 0;
        }
        return amount;
    }


    //攻击技能触发
    @ModifyVariable(method = "damage", at = @At(value = "FIELD", target = "Lnet/minecraft/entity/LivingEntity;despawnCounter:I"), ordinal = 0, argsOnly = true)
    private float applyOnAttackAbilities(float amount) {
        if (source == null) return amount;
        Entity attacker = source.getAttacker();
        if (attacker instanceof LivingEntity livingEntity) {
            Ref<Float> floatRef = new Ref<>(amount);
            ActionResult result = onAttackCallback.EVENT.invoker()
                    .onAttack(livingEntity, (LivingEntity) (Object) this, source, floatRef, source instanceof EntityDamageSource, source instanceof ProjectileDamageSource);
            amount = floatRef.getValue();
            if (result == ActionResult.FAIL) {
                candamage = false;
                return 0;
            }
        }
        return amount;
    }

    @Inject(method = "damage", at = @At(value = "FIELD", target = "Lnet/minecraft/entity/LivingEntity;despawnCounter:I", ordinal = 0), cancellable = true)
    private void isCandamage(DamageSource source, float amount, CallbackInfoReturnable<Boolean> cir) {
        if (!candamage) {
            cir.setReturnValue(false);
            cir.cancel();
        }
    }

    //被动技能触发
    @Inject(method = "tick", at = @At("TAIL"), cancellable = true)
    private void applyPassiveAbilities(CallbackInfo ci) {
        ActionResult result = passiveApplyCallback.EVENT.invoker().passiveApply((LivingEntity) (Object) (this));
        if (result == ActionResult.FAIL) {
            ci.cancel();
        }
    }

    //主动技能触发
    @Inject(method = "tick", at = @At("TAIL"), cancellable = true)
    private void useActiveAbilities(CallbackInfo ci) {
        ActionResult result = onAbilityUseCallback.EVENT.invoker().onAbilityUse((LivingEntity) (Object) this);
        if (result == ActionResult.FAIL) {
            ci.cancel();
        }
    }

    //治疗技能触发
    @ModifyVariable(method = "heal", at = @At("HEAD"), ordinal = 0, argsOnly = true)
    private float useActiveAbilities(float amount) {
        cantreat = true;
        Ref<Float> floatRef = new Ref<>(amount);
        ActionResult result = onTreatCallback.EVENT.invoker().onTreat((LivingEntity) (Object) this, floatRef);
        amount = floatRef.getValue();
        if (result == ActionResult.FAIL) {
            cantreat = false;
            return 0;
        }
        return amount;
    }

    @Inject(method = "heal", at = @At("HEAD"), cancellable = true)
    private void isCantreat(float amount, CallbackInfo ci) {
        if (!cantreat) {
            ci.cancel();
        }
    }


    //死亡技能触发
    @Inject(method = "onDeath", at = @At(value = "INVOKE_ASSIGN", target = "net/minecraft/entity/damage/DamageSource.getAttacker ()Lnet/minecraft/entity/Entity;", opcode = Opcodes.INVOKEVIRTUAL, ordinal = 0, shift = At.Shift.BEFORE), cancellable = true)
    private void onDeathAbilities(DamageSource source, CallbackInfo ci) {
        LivingEntity entity = source.getAttacker() instanceof LivingEntity livingEntity ? livingEntity : null;
        if (entity == null) entity = this.getPrimeAdversary();
        ActionResult result = onDeathCallback.EVENT.invoker().onDeath(entity, (LivingEntity) (Object) (this), source, source instanceof EntityDamageSource, source instanceof ProjectileDamageSource);
        if (result == ActionResult.FAIL) {
            if (((LivingEntity) (Object) (this)).isDead()) {
                ((LivingEntity) (Object) (this)).setHealth(1);
            }
            ci.cancel();
        }
    }

    //brodong: add damage Particle when apply damage
    @Environment(value = EnvType.CLIENT)
    @Inject(method = "applyDamage", at = @At("TAIL"), cancellable = true)
    protected void applyDamageAddParticle(DamageSource source, float amount, CallbackInfo ci) {
        if (amount >= 0.01f && amount <= 1e+20f) {
            DamageNumParticle particle = new DamageNumParticle(this, amount, source.name);
            DamageNumRenderer.PARTICLES.add(particle);
        }
        //System.out.print(particle.damageSource);
        //System.out.println(particle.damage);
    }

    //brodong: add heal Particle when apply damage
    @Environment(value = EnvType.CLIENT)
    @Inject(method = "heal", at = @At("TAIL"), cancellable = true)
    protected void HealAddParticle(float amount, CallbackInfo ci) {
        if (amount >= 0.01f) {
            DamageNumParticle particle = new DamageNumParticle(this, amount, "Heal");
            DamageNumRenderer.PARTICLES.add(particle);
        }
        //System.out.print(particle.damageSource);
        //System.out.println(particle.damage);
    }
}
